package cn.wd.user.service.impl;


import cn.hutool.json.JSONUtil;
import cn.wd.common.constant.DefaultValues;
import cn.wd.common.util.CollUtils;
import cn.wd.common.util.StrUtils;
import cn.wd.model.LoginInfo;
import cn.wd.model.ServiceException;
import cn.wd.service.TokenService;
import cn.wd.user.domain.entity.Department;
import cn.wd.user.domain.entity.Employee;
import cn.wd.user.domain.mapper.EmployeeMapper;
import cn.wd.user.model.LoginBody;
import cn.wd.user.qo.EmployeeQueryObject;
import cn.wd.user.service.IDepartmentService;
import cn.wd.user.service.IEmployeeService;
import cn.wd.user.utils.TokenManager;
import cn.wd.user.vo.BatchData;
import cn.wd.util.CacheKey;
import cn.wd.util.RedisService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {

    private final EmployeeMapper mapper;
    private final IDepartmentService departmentService;
    private final RedisService redisService;
    private final TokenService tokenService;
    public EmployeeServiceImpl(EmployeeMapper mapper, IDepartmentService departmentService, RedisService redisService, TokenService tokenService) {
        this.mapper = mapper;
        this.departmentService = departmentService;
        this.redisService = redisService;
        this.tokenService = tokenService;
    }

    @Override
    public String login(String username, String password) {

        // 1. 基于用户名去数据库查询用户信息
        Employee employee = mapper.selectByEmployeeName(username);

        // 2. 如果信息不存在, 提示用户名或密码错误
        if (employee == null) {
            log.error("[员工服务] 登录失败, 用户名输入错误, 用户名={}, 提交的密码={}", username, password);
            throw new RuntimeException("用户名或密码错误");
        }

        // 3. 判断密码是否正确, 如果不正确, 提示用户名或密码错误
        if (!password.equals(employee.getPassword())) {
            log.error("[员工服务] 登录失败, 密码输入错误, 用户名={}, 提交的密码={}", username, password);
            throw new RuntimeException("用户名或密码错误");
        }

        // 4. 生成 token, 并将当前用户存入 map
        String token = TokenManager.getToken();

        LoginInfo loginInfo = new LoginInfo();
        // 将 employee 中所有与 loginInfo 对象中属性名相同的属性值全部 copy 到 loginInfo
        BeanUtils.copyProperties(employee, loginInfo);

        TokenManager.put(token, loginInfo);

        // 5. 返回 token
        return token;
    }

    public String login(LoginBody loginBody) {
        Employee employee = mapper.selectByEmployeeName(loginBody.getUsername());
        if (employee == null) {
            throw new ServiceException(String.format("该用户%s尚未注册", loginBody.getUsername()));
        }
        String lockedKey = CacheKey.getLockedKey(loginBody.getUsername());
        String maxErrorKey = CacheKey.getMaxErrorKey(loginBody.getUsername());
        Boolean locked = redisService.getIfAbsentDefault(lockedKey, Boolean.FALSE);
        if (locked) {
            Long expire = redisService.getExpire(lockedKey) / 60;
            throw new ServiceException("账号已被锁定，" + expire + "分钟后再试");
        }
        if (!loginBody.getPassword().equals(employee.getPassword())) {
            Integer maxErrorCount = redisService.getIfAbsentDefault(maxErrorKey, DefaultValues.MAX_ERROR_COUNT);

            redisService.setEx(maxErrorKey, --maxErrorCount, 30, TimeUnit.MINUTES);
            if (maxErrorCount <= 0) {
                redisService.setEx(lockedKey, Boolean.TRUE, 30, TimeUnit.MINUTES);
                throw new ServiceException("账号已被锁定，30分钟后再试");
            }
            throw new ServiceException(String.format("用户名或密码错误，你还剩%s次机会", maxErrorCount));
        }

        redisService.delete(lockedKey);
        redisService.delete(maxErrorKey);

        LoginInfo loginInfo = new LoginInfo();
        // 将 employee 中所有与 loginInfo 对象中属性名相同的属性值全部 copy 到 loginInfo
        BeanUtils.copyProperties(employee, loginInfo);
        String token = tokenService.createToken(loginInfo);
        loginInfo.setToken(token);

        TokenManager.put(token, loginInfo);

        String redisTokenKey = CacheKey.getTokenKey(loginBody.getUsername());
        redisService.setEx(redisTokenKey, token, 15, TimeUnit.DAYS);
        redisService.setEx(CacheKey.getTokenUserKey(token), loginInfo, 15, TimeUnit.DAYS);
        return token;
    }

    // @Override
    // public void save(Employee obj) {
    //     mapper.insert(obj);
    // }
    //
    // @Override
    // public void deleteById(Long id) {
    //     mapper.deleteByPrimaryKey(id);
    // }
    //
    // @Override
    // public void updateById(Employee obj) {
    //     mapper.updateByPrimaryKey(obj);
    // }
    //
    // @Override
    // public Employee selectById(Long id) {
    //     return mapper.selectByPrimaryKey(id);
    // }
    //
    // @Override
    // public List<Employee> selectAll() {
    //     return mapper.selectAll();
    // }

    @Override
    public Page<Employee> listEmployeePage(EmployeeQueryObject qo) {
        LambdaQueryWrapper<Employee> employeeWrapper = new LambdaQueryWrapper<Employee>()
                .in(CollUtils.isNotEmpty(qo.getDeptIds()), Employee::getDeptId, qo.getDeptIds())
                .or(StrUtils.isNotEmpty(qo.getKeyword()), rap ->
                        rap.like(Employee::getUsername, qo.getKeyword())
                                .or()
                                .like(Employee::getNickname, qo.getKeyword())
                );
        Page<Employee> employeePage = baseMapper.selectCustomPage(qo, employeeWrapper);

        List<Long> deptIds = new ArrayList<>();
        Map<Long, Employee> employeeMap = new HashMap<>();
        for (Employee employee : employeePage.getRecords()) {
            deptIds.add(employee.getDeptId());
            employeeMap.put(employee.getDeptId(), employee);
        }
        LambdaQueryWrapper<Department> wrapper = new LambdaQueryWrapper<Department>().in(Department::getId, deptIds);
        List<Department> departmentList = departmentService.list(wrapper);
        for (Department department : departmentList) {
            Employee employee = employeeMap.get(department.getId());
            employee.setDept(department);
        }
        return employeePage;
    }


    @Override
    public List<Employee> selectByDepartmentPrimaryKey(Long deptId) {
        return mapper.selectByDepartmentPrimaryKey(deptId);
    }

    /**
     * 员工重置密码
     * @param id 员工 id
     * @param newPassword 原始密码
     * @param oldPassword 新密码
     */
    @Override
    public void restPassword(Long id, String newPassword, String oldPassword) {
        Employee employee = mapper.selectByPrimaryKey(id);
        if (! oldPassword.equals(employee.getPassword())) {
            log.error("[员工重置密码业务]： 员工输入的密码错误，");
            throw new RuntimeException("原始密码错误，请重新输入！");
        }
        mapper.restPassword(id, newPassword);
    }

    /**
     * 员工登录 修改用户信息中的更换头像操作
     * @param token 员工登录的token
     * @param avatar 员工新头像
     */
    @Override
    public void updateAvatar(String token, String avatar) {
        LoginInfo info = TokenManager.get(token);
        if (info != null) {
            mapper.updateAvatar(info.getId(), avatar);
        }
    }

    @Override
    public void batchList(BatchData batchData) {
        mapper.updateEmployeeDeptId(batchData.getDeptId(), batchData.getEmpIds());
    }
}
